package org.minbox.framework.command.execute;

import lombok.Getter;
import org.minbox.framework.command.execute.output.ErrorOutput;
import org.minbox.framework.command.execute.output.Output;
import org.minbox.framework.command.execute.output.ResultOutput;
import org.minbox.framework.command.execute.shell.CmdShell;
import org.minbox.framework.command.execute.shell.LinuxShell;
import org.minbox.framework.command.execute.shell.MacShell;
import org.minbox.framework.command.execute.shell.Shell;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

/**
 * Command Line Class
 * Construct the executed command and the parameters required for execution,
 * and output the results of the execution and the error logs encountered during the execution
 *
 * @author 恒宇少年
 */
public class CommandLine {
    /**
     * the execute command
     */
    private String command;
    /**
     * Args required for command execution
     */
    private List<String> commandArgs = new LinkedList<>();
    /**
     * Shells for different operating systems
     *
     * @see LinuxShell
     * @see MacShell
     * @see CmdShell
     */
    private Shell shell;
    /**
     * {@link Process} of executing the command
     */
    @Getter
    private Process process;
    /**
     * Error output after command execution
     *
     * @see org.minbox.framework.command.execute.output.AbstractOutput
     * @see ErrorOutput
     */
    private Output errorOutput = new ErrorOutput();
    /**
     * Result output after command execution
     *
     * @see org.minbox.framework.command.execute.output.AbstractOutput
     * @see ResultOutput
     */
    private Output resultOutput = new ResultOutput();

    /**
     * Constructor privatization
     * create instance by {@link #instance()} method
     */
    private CommandLine() {
    }

    /**
     * Create new instance of {@link CommandLine}
     *
     * @return The {@link CommandLine} new instance object
     */
    public static CommandLine instance() {
        CommandLine commandLine = new CommandLine();
        commandLine.setDefaultShell();
        return commandLine;
    }

    /**
     * Set a custom execution error {@link Output}
     *
     * @param errorOutput error output instance
     * @return Current class instance
     */
    public CommandLine setErrorOutput(Output errorOutput) {
        this.errorOutput = errorOutput;
        return this;
    }

    /**
     * Set a custom execution result {@link Output}
     *
     * @param resultOutput result output instance
     * @return Current class instance
     */
    public CommandLine setResultOutput(ResultOutput resultOutput) {
        this.resultOutput = resultOutput;
        return this;
    }

    /**
     * Set command line instructions
     *
     * @param command {@link #command}
     * @return Current class instance
     */
    public CommandLine setCommand(String command) {
        this.command = command;
        return this;
    }

    /**
     * Add args for this execution
     *
     * @param args args array
     * @return Current class instance
     */
    public CommandLine addArgs(String[] args) {
        this.commandArgs.addAll(Arrays.asList(args));
        return this;
    }

    /**
     * Add a single args for this execution
     *
     * @param args a single args
     * @return Current class instance
     */
    public CommandLine addArgs(String args) {
        this.commandArgs.add(args);
        return this;
    }

    /**
     * Execute commands using an array
     * get shell execution commands and parameters according to different operating systems
     *
     * @return Current class instance
     */
    public CommandLine execute() {
        try {
            List<String> commandRawLine = new ArrayList<>();
            commandRawLine.add(shell.getShellCommand());
            commandRawLine.addAll(shell.getShellArgs());
            commandRawLine.add(this.command);
            commandRawLine.addAll(this.commandArgs);
            Process process = Runtime.getRuntime().exec(commandRawLine.toArray(new String[]{}));
            this.process = process;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return this;
    }

    /**
     * get error content after command line execution
     *
     * @return Error content
     */
    public String getErrorContent() {
        return errorOutput.output(this.process.getErrorStream());
    }

    /**
     * get result content after command line execution
     *
     * @return Result content
     */
    public String getResultContent() {
        return resultOutput.output(this.process.getInputStream());
    }


    /**
     * set the shell to execute based on os.name
     * use the {@link OperatingSystem#isOs(String)} method to determine the operating system
     *
     * @see OperatingSystem
     */
    private void setDefaultShell() {
        if (OperatingSystem.isOs(OperatingSystem.WINDOWS)) {
            this.shell = new CmdShell();
        } else if (OperatingSystem.isOs(OperatingSystem.MAC_OSX)) {
            this.shell = new MacShell();
        } else if (OperatingSystem.isOs(OperatingSystem.LINUX)) {
            this.shell = new LinuxShell();
        }
    }
}
